package com.kxmall.web.controller.rider.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kxmall.common.core.domain.model.LoginUser;
import com.kxmall.common.enums.*;
import com.kxmall.common.exception.ServiceException;
import com.kxmall.common.helper.LoginHelper;
import com.kxmall.common.helper.TokenReturnHelper;
import com.kxmall.common.utils.TokenUtils;
import com.kxmall.common.utils.BeanCopyUtils;
import com.kxmall.common.utils.IpUtil;
import com.kxmall.common.utils.redis.RedisUtils;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import com.kxmall.huishou.domain.bo.HsRecyclerApplyBo;
import com.kxmall.huishou.service.IHsRecyclerApplyService;
import com.kxmall.rider.domain.KxRider;
import com.kxmall.rider.domain.KxRiderCycle;
import com.kxmall.rider.domain.vo.KxRiderVo;
import com.kxmall.rider.domain.vo.RiderLoginDTO;
import com.kxmall.rider.domain.vo.RiderWxDTO;
import com.kxmall.rider.mapper.KxRiderCycleMapper;
import com.kxmall.rider.mapper.KxRiderMapper;
import com.kxmall.system.service.ISysConfigService;
import com.kxmall.user.domain.KxUser;
import com.kxmall.user.domain.vo.KxUserVo;
import com.kxmall.web.controller.rider.service.RiderWxLoginService;
import com.kxmall.wechat.WxMaConfiguration;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @description:
 * @author: kxmall
 * @date: 2020/10/23 11:29
 **/
@Service
public class RiderWxLoginServiceImpl implements RiderWxLoginService {

    private static final Logger logger = LoggerFactory.getLogger(RiderWxLoginServiceImpl.class);

    private OkHttpClient okHttpClient = new OkHttpClient();



    @Resource
    private ISysConfigService configService;

    @Resource
    private KxRiderMapper riderMapper;

    @Resource
    private KxRiderCycleMapper riderCycleMapper;
    @Resource
    private IHsRecyclerApplyService iHsRecyclerApplyService;


    /**
     * 骑手微信小程序第三方登录
     */
    @Override
    public KxRiderVo wechatLogin(String ip, RiderWxDTO riderWxDTO){
        try {
            String body;
            if (StringUtils.isEmpty(riderWxDTO.getOpenid())) {
                //微信第三方登录
                String code = riderWxDTO.getCode();
                body = okHttpClient.newCall(new Request.Builder()
                        .url("https://api.weixin.qq.com/sns/jscode2session?appid=" + configService.selectConfigByKey("rider_mini_appId") +
                                "&secret=" + configService.selectConfigByKey("rider_mini_appSecret") +
                                "&grant_type=authorization_code&js_code=" + code).get().build()).execute().body().string();
            } else {
                //登录过一次
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("errcode", 0);
                jsonObject.put("openid", riderWxDTO.getOpenid());
                jsonObject.put("session_key", riderWxDTO.getSession_key());
                body = jsonObject.toJSONString();
            }

            JSONObject jsonObject = JSONObject.parseObject(body);
            Integer errcode = jsonObject.getInteger("errcode");
            if (errcode == null || errcode == 0) {
                String miniOpenId = jsonObject.getString("openid");
                String sessionKey = jsonObject.getString("session_key");
                String phone = riderWxDTO.getPhone();
                QueryWrapper<KxRider> queryWrapper = new QueryWrapper<KxRider>();
                queryWrapper.and(q->q.eq("open_id", miniOpenId).or().eq("phone",phone));
                List<KxRider> riderDOS = riderMapper.selectList(queryWrapper);
                KxRider riderDO;
                if (CollectionUtils.isEmpty(riderDOS)) {
                    //若用户为空，则注册此用户
                    KxRider kxRider = addRiderUser(ip, miniOpenId);
                    KxRiderVo riderDTO = new KxRiderVo();
                    riderDTO.setOpenId(miniOpenId);
                    riderDTO.setLoginType(RiderLoginType.MP_WEIXIN.getCode());
                    riderDTO.setSessionKey(sessionKey);
                    //增加申请列表
                    HsRecyclerApplyBo bo = new HsRecyclerApplyBo();
                    bo.setUserId(kxRider.getId());
                    bo.setName(kxRider.getName());
                    bo.setPhone(kxRider.getPhone());
                    iHsRecyclerApplyService.insertByBo(bo);
                    return riderDTO;
                } else {
                    // 已经注册的用户
                    riderDO = riderDOS.get(0);
                    if (riderDO.getPhone().equals(phone)) {
                        logger.info("手机号相同");
                    }
                    if (riderDO.getOpenId().equals(miniOpenId)) {
                        logger.info("openId相同");
                    }
                    KxRider riderUpdateDO = new KxRider();
                    riderUpdateDO.setId(riderDO.getId());
                    riderUpdateDO.setGmtLastLogin(new Date());
                    riderUpdateDO.setLastLoginIp(ip);
                    riderUpdateDO.setOpenId(miniOpenId);
                    riderUpdateDO.setPhone(phone);
                    if (!StringUtils.isEmpty(riderWxDTO.getPhone()) && ObjectUtils.notEqual(riderDO.getPhone(), riderWxDTO.getPhone())) {
                        riderUpdateDO.setPhone(riderWxDTO.getPhone());
                    }
                    if (ObjectUtils.notEqual(riderDO.getName(), riderWxDTO.getNickName())) {
                        riderUpdateDO.setName(riderWxDTO.getNickName());
                    }
                    if (ObjectUtils.notEqual(riderDO.getAvatarUrl(), riderWxDTO.getAvatarUrl())) {
                        riderUpdateDO.setAvatarUrl(riderWxDTO.getAvatarUrl());
                    }
                    riderMapper.updateById(riderUpdateDO);

                    if (riderDO.getState() ==  RiderStatusType.ABORT.getCode()) {
                        throw new ServiceException("骑手处于禁用状态，请联系管理员");
                    }

                    KxRiderVo riderVo = new KxRiderVo();
                    riderVo.setState(riderDO.getState());
                    riderVo.setSessionKey(sessionKey);
                    BeanCopyUtils.copy(riderDO, riderVo);

                    // 此处可根据登录用户的数据不同 自行创建 loginUser
                    LoginUser loginUser = buildLoginUser(riderVo);
                    // 生成token
                    LoginHelper.loginByDevice(loginUser, DeviceType.RIDER);
                    riderVo.setAccessToken(StpUtil.getTokenValue());
                    return riderVo;
                }
            } else {
                logger.info("[微信登录] 回复失败 回复报文：" + body);
                throw new ServiceException("第三方登录期望之外的错误");
            }
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            logger.error("[骑手小程序登录] 异常", e);
            throw new ServiceException("用户第三方登录失败");
        }

    }
    @Override
    public KxRiderVo miniPhoneLogin(String code, String encryptedData, String iv) {
        try {
            WxMaService riderWxMaService = WxMaConfiguration.getRiderWxMaService();
            WxMaJscode2SessionResult session = riderWxMaService.getUserService().getSessionInfo(code);

            // 验证session是否有效
            if (session == null || session.getSessionKey() == null) {
                throw new ServiceException("骑手微信登录失败，无法获取session信息");
            }

            String sessionKey = session.getSessionKey();
            String openid = session.getOpenid();

            // 添加日志记录
            System.out.println(String.format(
                "骑手微信小程序登录 - openid: %s, sessionKey存在: %s",
                openid, sessionKey != null
            ));

            WxMaPhoneNumberInfo phoneNumberInfo;
            try {
                phoneNumberInfo = riderWxMaService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
            } catch (Exception e) {
                System.err.println(String.format(
                    "骑手获取微信手机号失败 - sessionKey: %s, error: %s",
                    sessionKey, e.getMessage()
                ));
                throw new ServiceException("获取手机号失败，请检查授权信息是否正确");
            }

            // 验证手机号信息是否有效
            if (phoneNumberInfo == null || phoneNumberInfo.getPhoneNumber() == null) {
                throw new ServiceException("获取手机号失败，返回信息为空");
            }

            String phoneNumber = phoneNumberInfo.getPhoneNumber();

        // 使用 openid 作为锁的键，防止同一用户并发登录导致的串号问题
        Lock riderLoginLock = RedisUtils.lock("RIDER_LOGIN_LOCK:" + openid);
        try {
            boolean isLocked = riderLoginLock.tryLock(10, TimeUnit.SECONDS);
            if (!isLocked) {
                throw new ServiceException("骑手登录系统繁忙，请稍后重试");
            }

            try {

        QueryWrapper<KxRider> queryWrapper = new QueryWrapper<KxRider>();
        queryWrapper.and(q->q.eq("open_id", openid).or().eq("phone",phoneNumber));
        List<KxRider> riderDOS = riderMapper.selectList(queryWrapper);
        KxRider riderDO;
        if (CollectionUtils.isEmpty(riderDOS)) {
            //若用户为空，则注册此用户
            KxRider kxRider = addRiderUser(IpUtil.getLocalIP(), openid);
            kxRider.setOpenId(openid);
            kxRider.setLoginType(RiderLoginType.MP_WEIXIN.getCode());
            kxRider.setPhone(phoneNumber);
            kxRider.setName("微信用户"+ openid.substring(0, 5));
            riderMapper.updateById(kxRider);

            //增加申请列表
            HsRecyclerApplyBo bo = new HsRecyclerApplyBo();
            bo.setUserId(kxRider.getId());
            bo.setName(kxRider.getName());
            bo.setPhone(kxRider.getPhone());
            iHsRecyclerApplyService.insertByBo(bo);

            throw new ServiceException("新注册账号需要等待审核通过");
        }// 已经注册的用户
        riderDO = riderDOS.get(0);
        if (phoneNumber.equals(riderDO.getPhone())) {
            logger.info("手机号相同");
        }
        if (openid.equals(riderDO.getOpenId())) {
            logger.info("openId相同");
        }
        KxRider riderUpdateDO = new KxRider();
        riderUpdateDO.setId(riderDO.getId());
        riderUpdateDO.setGmtLastLogin(new Date());
        riderUpdateDO.setLastLoginIp(IpUtil.getLocalIP());
        riderUpdateDO.setOpenId(openid);
        riderUpdateDO.setPhone(phoneNumber);
        if (StrUtil.isBlank(riderDO.getName())){
            riderUpdateDO.setName("微信用户"+ openid.substring(0, 5));
        }
        riderMapper.updateById(riderUpdateDO);

        if (riderDO.getState() ==  RiderStatusType.ABORT.getCode()) {
            throw new ServiceException("骑手处于禁用状态，请联系管理员");
        }

        KxRiderVo riderVo = new KxRiderVo();
        riderVo.setState(riderDO.getState());
        riderVo.setSessionKey(sessionKey);
        BeanCopyUtils.copy(riderDO, riderVo);

                // 此处可根据登录用户的数据不同 自行创建 loginUser
                LoginUser loginUser = buildLoginUser(riderVo);
                // 生成token
                LoginHelper.loginByDevice(loginUser, DeviceType.RIDER);
                // 安全获取Token用于返回（新增唯一性验证）
                String secureToken = TokenReturnHelper.getRiderUserToken(riderVo.getId());

                // 最终验证Token返回唯一性（新增）
                validateFinalRiderTokenReturn(secureToken, "rider_user", DeviceType.RIDER, riderVo.getId());

                riderVo.setAccessToken(secureToken);
                return riderVo;
            } catch (Exception e) {
                logger.error("骑手登录处理异常", e);
                throw new ServiceException("骑手登录失败，请稍后重试");
            } finally {
                riderLoginLock.unlock();
            }
        } catch (InterruptedException e) {
            logger.error("获取骑手登录锁被中断", e);
            Thread.currentThread().interrupt();
            throw new ServiceException("骑手登录系统繁忙，请稍后重试");
        }
        } catch (WxErrorException e) {
            logger.error("骑手微信API调用失败", e);
            throw new ServiceException("骑手微信登录失败：" + e.getMessage());
        } catch (ServiceException e) {
            // 重新抛出业务异常
            throw e;
        } catch (Exception e) {
            logger.error("骑手微信小程序登录异常", e);
            throw new ServiceException("骑手登录失败，请稍后重试");
        }
    }

    private LoginUser buildLoginUser(KxRiderVo riderDTO) {
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(riderDTO.getId());
        loginUser.setUsername(riderDTO.getName());
        loginUser.setUserType(UserType.RIDER_USER.getUserType());
        loginUser.setOpenId(riderDTO.getOpenId());
        loginUser.setLoginType(riderDTO.getLoginType());
        return loginUser;
    }

    /**
     * 新增骑手用户
     */
    private KxRider addRiderUser(String ip, String miniOpenId) {
        KxRider riderDO;
        Date now = new Date();
        riderDO = new KxRider();
        riderDO.setLoginType(RiderLoginType.MP_WEIXIN.getCode());
        riderDO.setWorkState(RiderWorkStateType.REST.getCode());
        riderDO.setState(RoleStatusType.LOCK.getCode());

        riderDO.setDeliveryStart("8:30");
        riderDO.setDeliveryEnd("23:00");
        riderDO.setOpenId(miniOpenId);
        riderDO.setLastLoginIp(ip);
        riderDO.setGmtLastLogin(now);
        riderDO.setUpdateTime(now);
        riderDO.setCreateTime(now);
        riderDO.setStorageId(11L);
        riderMapper.insert(riderDO);
        //上班周期
        for (int i = 0; i < 7; i++) {
            KxRiderCycle riderCycleDO = new KxRiderCycle();
            riderCycleDO.setRiderId(riderDO.getId());
            riderCycleDO.setWeekNumber((long) (i + 1));
            riderCycleMapper.insert(riderCycleDO);
        }
        return riderDO;
    }

    @Override
    public String decryptS5(String ip, String raw){
        JSONObject thirdPartJsonObject = JSONObject.parseObject(raw);
        String sSrc = thirdPartJsonObject.getString("encryptedData");
        String encodingFormat = "utf-8";
        String sKey = thirdPartJsonObject.getString("sessionkey");
        String ivParameter = thirdPartJsonObject.getString("iv");
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] buffer = decoder.decodeBuffer(sKey);
            SecretKeySpec skeySpec = new SecretKeySpec(buffer, "AES");
            IvParameterSpec iv = new IvParameterSpec(decoder.decodeBuffer(ivParameter));
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] myendicod = decoder.decodeBuffer(sSrc);
            byte[] original = cipher.doFinal(myendicod);
            System.out.println(new String(original, encodingFormat));
            return new String(original, encodingFormat);
        } catch (Exception ex) {
            logger.error("[手机号解密] 异常", ex);
            throw new ServiceException("手机号解密异常");
        }
    }

    @Override
    public RiderLoginDTO getRiderInfo(Long riderId){
        KxRider riderDO = riderMapper.selectById(riderId);
        RiderLoginDTO riderLoginDTO = new RiderLoginDTO();
        BeanUtils.copyProperties(riderDO, riderLoginDTO);
        riderLoginDTO.setState(riderDO.getState());
        return riderLoginDTO;
    }


    @Override
    public boolean updateById(KxRider kxRider){

        return riderMapper.updateById(kxRider) >0;
    }

    /**
     * 最终验证骑手Token返回唯一性
     */
    private void validateFinalRiderTokenReturn(String tokenValue, String userType, DeviceType deviceType, Long userId) {
        try {
            // 1. 验证Token与指定用户的Token一致性
            String expectedToken = TokenUtils.getTokenByUserTypeAndId(userType, deviceType, userId);

            if (!tokenValue.equals(expectedToken)) {
                throw new ServiceException("骑手Token返回一致性验证失败 - 期望: " + tokenValue.substring(0, 10) + "..., 实际: " +
                    (expectedToken != null ? expectedToken.substring(0, 10) + "..." : "null"));
            }

            // 2. 验证Token与用户信息一致性
            String currentLoginId = StpUtil.getLoginIdAsString();
            String expectedLoginId = userType + ":" + userId;
            if (!expectedLoginId.equals(currentLoginId)) {
                throw new ServiceException("骑手Token用户信息一致性验证失败 - 期望: " + expectedLoginId + ", 实际: " + currentLoginId);
            }

            // 3. 验证Token与设备信息一致性
            String currentDevice = StpUtil.getLoginDevice();
            if (!deviceType.getDevice().equals(currentDevice)) {
                throw new ServiceException("骑手Token设备信息一致性验证失败 - 期望: " + deviceType.getDevice() + ", 实际: " + currentDevice);
            }

            // 4. 验证Token会话完整性
            if (StpUtil.getTokenSession().get("loginUser") == null) {
                throw new ServiceException("骑手Token会话完整性验证失败 - 会话信息缺失");
            }

            // 5. 验证Token有效期
            long tokenTimeout = StpUtil.getTokenTimeout();
            if (tokenTimeout <= 0) {
                throw new ServiceException("骑手Token有效期验证失败 - Token已过期");
            }

            // 6. 记录最终验证成功日志
            System.out.println(String.format(
                "✅ 骑手Token返回最终验证通过 - 用户: %s, 设备: %s, Token前缀: %s, 有效期: %d秒",
                expectedLoginId, deviceType.getDevice(),
                tokenValue.substring(0, Math.min(10, tokenValue.length())) + "...",
                tokenTimeout
            ));

        } catch (Exception e) {
            System.err.println(String.format(
                "🚨 骑手Token返回最终验证失败 - 用户: %s:%s, 设备: %s, 错误: %s",
                userType, userId, deviceType.getDevice(), e.getMessage()
            ));
            throw new ServiceException("骑手Token返回最终验证失败: " + e.getMessage());
        }
    }
}
